import {
    DesignViewModelService, SchemaService, DomService, DesignViewModel,
    FormSchemaEntityFieldType$Type
} from '@farris/designer-services';
import { DesignerHostSettingService, DgControl } from '@farris/designer-services';
import { merge, get, set } from 'lodash-es';
import { Injector } from '@angular/core';

import { SchemaChangeEntity } from './refresh.service';

/**
 * 更新表单Schema后，同步控件的服务
 */
export class RefreshFormAfterSchemaChangedService {

    private domService: DomService;
    private schemaService: SchemaService;
    private dgViewModelService: DesignViewModelService;
    private controlCreatorService: any;
    private schemaDOMMapping: any;

    constructor(
        private injector: Injector) {
        this.domService = this.injector.get(DomService);
        this.schemaService = this.injector.get(SchemaService);
        this.dgViewModelService = this.injector.get(DesignViewModelService);

        const designerHostService = this.injector.get(DesignerHostSettingService);
        this.schemaDOMMapping = designerHostService.designerHost.getService('SchemaDOMMapping');
        this.controlCreatorService = designerHostService.designerHost.getService('ControlCreatorService');

    }

    /**
     * 刷新schema后更新DOM结构：原则：变更的属性不在VM.fieldSchema里
     * @param changes
     */
    refreshAfterSchemaChange(changes: SchemaChangeEntity) {

        const dgViewModels = this.dgViewModelService.getDgViewModels();
        dgViewModels.forEach(dgViewModel => {
            if (!dgViewModel.fields || dgViewModel.fields.length === 0) {
                return;
            }
            dgViewModel.fields.forEach(field => {
                // 1、 获取当前VM对应的组件中绑定该字段的所有控件
                const controls = this.domService.getControlsInCmpWidthBinding(dgViewModel.id, field.id);
                // 字段不存在：若是table类型，则需要自动移除编辑器
                if (field.isSchemaRemoved) {
                    this.updateTableEditorAfterSchemaFieldRemoved(controls, dgViewModel.id);
                }
                const fieldChanges = changes[field.id];


                if (!fieldChanges) {
                    return;
                }

                if (!controls && controls.length === 0) {
                    return;
                }

                // 2、判断是否需要替换控件：字段类型更改并且后替换控件
                // udt和关联字段，不要同步，需要手动删除。
                const typeChange = fieldChanges.find(c => c.propPath === 'type.$type');
                const editorChange = fieldChanges.find(c => c.propPath === 'editor' || c.propPath === 'editor.$type');
                const multiLanguageChange = fieldChanges.find(c => c.propPath === 'multiLanguage' && c.newValue === true);

                // ① 替换控件
                if (typeChange && editorChange) {
                    this.changeControlType(controls, field, dgViewModel);
                    return;
                }

                // 若多语属性由false-->true，需要替换控件
                if (multiLanguageChange && editorChange) {
                    this.changeControlType(controls, field, dgViewModel);
                    return;
                }

                // ② 不需要替换控件
                this.updateControlsBySchemaChange(fieldChanges, controls, field, dgViewModel.id);
            });
        });

    }
    /**
     * 对于table中的输入控件，若绑定字段被移除，需要自动清除单元格的绑定，因为目前table显示视图模型
     * @param controls 
     * @param viewModelId 
     */
    private updateTableEditorAfterSchemaFieldRemoved(controls: any[], viewModelId: string) {
        if (controls && controls.length) {
            const fieldIds = [];
            controls.forEach(control => {
                if (control.showInTable && control.binding && control.binding.field) {
                    fieldIds.push(control.binding.field);
                    this.assignControlDom(control, { type: null, binding: null });
                }
            });

            this.deleteFieldById(viewModelId, fieldIds);
        }
    }

    /**
     * 根据控件变更集更新控件。
     * @param fieldChanges 字段变更集
     * @param controls 控件列表
     * @param dgField 字段
     * @param viewModelId 模型ID
     */
    private updateControlsBySchemaChange(fieldChanges, controls, dgField, viewModelId) {
        let updateChanges = [];
        const viewModelField = this.domService.getViewModelFieldById(viewModelId, dgField.id);
        const fieldSchema = viewModelField.fieldSchema;

        // ① 收集需要在控件上变更的属性：VM上没有变更记录 或者 变更的属性不在VM的变更记录中
        if (!fieldSchema) { // 字段没有变更记录
            updateChanges = updateChanges.concat(fieldChanges)
        } else {
            // 判断变更是否在fieldSchema中，若在不更control，若不在，更control(因为表单对控件的变更优先于VO的变更)
            fieldChanges.forEach(change => {
                const propInVm = get(fieldSchema, change.propPath, 'notFound');
                if (propInVm === 'notFound') {
                    updateChanges.push(change);
                }
            });
        }
        if (updateChanges.length === 0) {
            return;
        }

        // ③ 更新control属性
        controls.forEach(control => {
            const domChangeset = this.getDomChangeBySchemaChange(control, updateChanges);
            merge(control, domChangeset);

            // 若schema bindingField 变更，需要同步viewmodel field中的fieldName值
            const bindingFieldProp = updateChanges.find(c => c.propPath === 'bindingField');
            if (bindingFieldProp) {
                viewModelField.fieldName = bindingFieldProp.newValue;
            }

            // 数字类型从普通浮点数转为大数字：不更换控件，但要修改控件bigNumber属性
            if (control.type === DgControl.NumericBox.type) {
                const bigNumber = updateChanges.find(c => c.propPath === 'type.$type' && c.newValue.includes('NumericType'));
                if (bigNumber) {
                    control.bigNumber = bigNumber.newValue === FormSchemaEntityFieldType$Type.BigNumericType;
                }
            }
            if ((control.type === 'GridField' || control.type === 'TreeGridField') &&
                control.editor && control.dataType === 'number') {
                const bigNumber = updateChanges.find(c => c.propPath === 'type.$type' && c.newValue.includes('NumericType'));
                if (bigNumber) {
                    control.editor.bigNumber = bigNumber.newValue === FormSchemaEntityFieldType$Type.BigNumericType;
                }
            }
        });

    }

    /**
     * schema变更字段类型后替换DOM控件
     * @param controls
     * @param dgField
     */
    private changeControlType(controls, dgField, dgViewModel: DesignViewModel) {
        // 删除VM上针对原字段的修改
        const schema = this.schemaService.getFieldByIDAndVMID(dgField.id, dgViewModel.id);
        dgViewModel.clearFieldChange(schema.schemaField, dgField);

        this.clearExpressionByFieldId([dgField.id]);

        controls.forEach(control => {
            if (dgField.$type === 'ComplexField') {
                // 复杂字段相关控件由用户在视图模型中手动删除
                // this.notifyService.warning({
                //     title: '系统提示', msg: '请删除复杂字段【' + dgField.name + '】相关控件！', timeout: 3000
                // } as NotifyOptions);

                // table内的输入控件自动移除单元格的绑定数据
                if (control.showInTable) {
                    this.assignControlDom(control, { type: null, binding: null });
                    this.deleteFieldById(dgViewModel.id, [dgField.id]);
                }
                return;
            }
            if (control.type === DgControl.GridField.type || control.type === DgControl.TreeGridField.type) {
                const editable = control.editor ? true : false;
                const metadata = this.controlCreatorService.createGridFieldBySchemaFeild(dgField, '',
                    editable, control.type, control.controlSource);
                // 保留原id、dataField
                metadata.id = control.id;
                metadata['dataField'] = control.dataField;
                if (editable) {
                    metadata['editor']['id'] = control.editor.id;
                }
                // 仍使用原来的controlSource和frozen属性
                if (control.controlSource) {
                    metadata.controlSource = control.controlSource;
                }
                if (control.frozen) {
                    metadata.frozen = control.frozen;
                }
                this.assignControlDom(control, metadata);
            } else {
                let metadata;
                if (control.showInTable) {
                    metadata = this.controlCreatorService.createTableTdControlBySchemaFeild(dgField, '');
                } else {
                    metadata = this.controlCreatorService.createControlBySchemaFeild(dgField, '');
                }
                // 保留原id、name、样式
                metadata.id = control.id;
                metadata['title'] = control['title'];
                metadata['appearance'] = control.appearance;

                // 只读属性：除非配置为表达式，否则都沿用旧控件的只读值
                if (!(control.readonly && control.readonly.type === 'Expression')) {
                    metadata['readonly'] = control.readonly;
                }
                this.assignControlDom(control, metadata);


            }
        });


    }
    /**
     * 根据schema的变更获取DOM对应的变更
     * @param controlDom 控件DOM
     * @param changeObjects schema变更集合
     */
    private getDomChangeBySchemaChange(controlDom, changeObjects) {
        const schemaChangePaths = changeObjects.map(c => c.propPath);
        const mappingArray = this.schemaDOMMapping.mappingDomPropAndSchemaProp(controlDom);
        const domChangeset = {};
        mappingArray.map(m => {
            if (schemaChangePaths.includes(m.schemaField)) {
                const changeObject = changeObjects.find(c => c.propPath === m.schemaField);

                // 只读、必填属性单独处理： 需要同步的场景有：1、控件设置为boolean时（状态机、表达式等不更新）2、vo上属性由false改成true
                if (m.schemaField === 'readonly') {
                    if (typeof (controlDom.readonly) === 'boolean' || changeObject.newValue === true) {
                        set(domChangeset, m.domField, changeObject.newValue);
                    }
                } else if (m.schemaField === 'require') {
                    if (typeof (controlDom.require) === 'boolean' || changeObject.newValue === true) {
                        set(domChangeset, m.domField, changeObject.newValue);
                    }
                } else {
                    set(domChangeset, m.domField, changeObject.newValue);
                }
            }
        });

        return domChangeset;
    }



    private assignControlDom(oldControlDom, metadata) {
        for (const key of Object.keys(oldControlDom)) {
            delete oldControlDom[key];
        }
        Object.assign(oldControlDom, metadata);

    }
    /**
     * 根据VMID和字段ID删除字段
     * @param viewModelId VMID
     * @param fieldIdList 字段ID列表
     */
    private deleteFieldById(viewModelId: string, fieldIdList: string[]) {
        const dgViewModel = this.dgViewModelService.getDgViewModel(viewModelId);
        if (!dgViewModel) { return; }

        dgViewModel.removeField(fieldIdList);


        this.clearExpressionByFieldId(fieldIdList);

    }

    /**
     * 若字段配置了表达式，需要删除表达式
     */
    private clearExpressionByFieldId(fieldIdList: string[]) {
        if (this.domService.expressions && this.domService.expressions.length && fieldIdList.length) {
            this.domService.expressions = this.domService.expressions.filter(e => !fieldIdList.includes(e.fieldId));
        }

    }
}
